接到一个需求,滚动一次鼠标,改变时间轴刻度。因为鼠标滚动一次会触发多次滚动事件,为了节约性能,就想到使用防抖。
这是我从网上随便摘的一段,看起来没什么问题:
export function debounce(func, wait = 500) {
let timer;
return (...args) => {
let context = this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
func.apply(context, args);
}, wait);
}
}
在vue里面使用:
// 滚动鼠标变化刻度,防抖。
changeTimeline: debounce(function (e) {
if (e.deltaY > 0) {
// 向上滚动
this.timelineType > 1 && this.setTimelineType(this.timelineType - 1);
} else {
// 向下滚动
this.timelineType < 3 && this.setTimelineType(this.timelineType + 1);
}
}, 500),
然而鼠标滚动时报了这个错:this丢失了
在网上查找各种方法,发现下面这样写没问题:在created()里把changeTimeline 重新包装一下
解决方法一
created() {
this.changeTimeline = debounce(this.changeTimeline, 1000);
},
methods: {
changeTimeline (e) {
if (e.deltaY > 0) {
this.timelineType > 1 && this.setTimelineType(this.timelineType - 1);
} else {
this.timelineType < 3 && this.setTimelineType(this.timelineType + 1);
}
}
}
问题是解决了,但是为什么呢?而且总觉得这不是最好的解决办法。
在网上查了各种有关this丢失的原因,最后发现问题出在debounce函数里返回函数使用了箭头函数,把返回函数改成普通函数的写法就没问题了。
解决方法二
export function debounce(func, wait = 500) {
let timer;
return function(...args) {
let context = this;
if (timer) {
clearTimeout(timer);
}
// setTimeout写成箭头函数还是普通函数都可以
timer = setTimeout(function () {
func.apply(context, args);
}, wait);
}
}
这里箭头函数导致this丢失的原因是,箭头函数是根据词法作用域,也就是箭头函数定义时的位置来决定this。在声明changeTimeline函数时,debounce内部的this还是undefined,它返回的箭头函数this继承了debounce的this,所以在鼠标滚动时调用这个箭头函数this还是undefined。
而普通函数的this是由代码运行时该函数的调用位置决定的。把debounce函数改成返回一个普通函数function(){},鼠标滚动时,调用该函数的组件实例已经创建完成,所以这个函数的this是Vue实例。
如果debounce返回箭头函数,就在created()里把changeTimeline 重新包装一下,这样debounce绑定的就是Vue实例,而返回的箭头函数会继承这个this。